import websocket
import json
import ssl
import time
import threading
from datetime import datetime
import redis
from dotenv import load_dotenv
import os
import redis
from collections import OrderedDict
import requests
from collections import defaultdict

load_dotenv()
binance_get_orderbook_interval=os.getenv('BINANCE_ORDERBOOK_INTERVAL')
if binance_get_orderbook_interval:
    binance_get_orderbook_interval=int(binance_get_orderbook_interval)
else:
    print("环境变量 'binance_get_orderbook_interval' 未设置")


binance_token_str = os.getenv('BINANCE_TOKEN')
if binance_token_str:
    binance_token_array = binance_token_str.split(',')
    binance_token_array = [item.strip() for item in binance_token_array]
else:
    print("环境变量 'BINANCE_TOKEN' 未设置")


group_size=os.getenv('BINANCE_GROUP_SIZE')
if group_size:
    group_size=float(group_size)
else:
    print("环境变量 'BINANCE_GROUP_SIZE' 未设置")


# 全局变量，保存本地的订单簿副本
orderbook = {
    "bids": {},  # 存储买单
    "asks": {}   # 存储卖单
}

pool = redis.ConnectionPool(host='localhost',port=6379, db=1)
r = redis.Redis(connection_pool=pool)

#递减排序
def sorted_ordered_dict_reverse(data):
    return OrderedDict(sorted(data.items(), key=lambda x: x[0], reverse=True))

#递增排序
def sorted_ordered_dict(data):
    return OrderedDict(sorted(data.items(), key=lambda x: x[0]))

def get_initial_orderbook(symbol):
    global orderbook
    url = f"https://fapi.binance.com/fapi/v1/depth?symbol={symbol}&limit=1000"
    response = requests.get(url)
    data = response.json()

    # with lock:
    orderbook["bids"] = {float(price): float(quantity) for price, quantity in data["bids"]}
    orderbook["asks"] = {float(price): float(quantity) for price, quantity in data["asks"]}
    
    return data["lastUpdateId"]

def updateOrderbook(event):
    global orderbook
    for bid in event["b"]:
        price = float(bid[0])
        quantity = float(bid[1])
        if quantity == 0:
            if price in orderbook["bids"]:
                del orderbook["bids"][price]
        else:
            orderbook["bids"][price] = quantity

    for ask in event["a"]:
        price = float(ask[0])
        quantity = float(ask[1])
        if quantity == 0:
            if price in orderbook["asks"]:
                del orderbook["asks"][price]
        else:
            orderbook["asks"][price] = quantity
    
    orderbook["asks"] = sorted_ordered_dict(orderbook["asks"])
    orderbook["bids"] = sorted_ordered_dict_reverse(orderbook["bids"])

def on_open(ws):
    print("binance websocket open")

def on_close(ws, close_status_code, close_msg):
    print("binance websocket closed")
    time.sleep(10)
    initialize_orderbook(binance_token_array[0])

def on_error(ws, error):
    print("binance websocket error")

def on_pong(ws, message):
    print(f"Received pong: {message}")

def on_ping(ws, message):
    print(f"Received ping from server: {message}")
    ws.send("pong")  # 回复一个pong

def initialize_orderbook(symbol):
    global orderbook
    orderbook = {
        "bids": {},  # 存储买单
        "asks": {}   # 存储卖单
    }
    lastUpdateId=None
    lastu=None

    def on_depth_update(ws, message):
        nonlocal lastUpdateId
        nonlocal lastu
        
        message = json.loads(message)
        event = message["data"]
        if lastu!=None and lastu==event["pu"]:
            updateOrderbook(event=event)
            lastu=event["u"]

        # 确保第一个 U <= lastUpdateId 且 u >= lastUpdateId 的事件之后才开始更新订单簿
        if lastUpdateId!=None:
            if event["U"] <= lastUpdateId <= event["u"]:
                print("binance 订单簿同步成功")
                updateOrderbook(event=event)
                lastu=event["u"]
                lastUpdateId=None
            else:
                print("binance 订单簿同步失败，重新同步")
                ws.close()

    # 订阅 WebSocket
    websocket_url = f"wss://fstream.binance.com/stream?streams={symbol.lower()}@depth"
    ws = websocket.WebSocketApp(websocket_url,
                                on_message=on_depth_update,
                                on_open=on_open,
                                on_close=on_close,
                                on_error=on_error)
    ws_thread = threading.Thread(target=ws.run_forever,kwargs={"sslopt": {"cert_reqs": ssl.CERT_NONE},"ping_interval":30,"ping_timeout":5})
    ws_thread.start()
    time.sleep(0.5)
    lastUpdateId = get_initial_orderbook(symbol)

def group_order(order):
    grouped_order = defaultdict(float)
    size=1/group_size
    for price, quantity in order.items():
        # 将价格转换为 float 并四舍五入到最近的 0.1 单位
        rounded_price = round(float(price) * size) / size
        # 将相同价格区间的数量相加
        grouped_order[rounded_price] =round(grouped_order[rounded_price]+quantity,3)
    # 将 defaultdict 转换为普通字典并打印结果
    grouped_order = dict(grouped_order)
    return grouped_order

def save_snapshoot(interval):
    while(True):
        time.sleep(interval)
        trading_pair=binance_token_array[0]
        current_time = datetime.now().strftime('%Y-%m-%d:%H:%M:%S')
        trade_data = {
            "time": current_time,
            "trading_pair": trading_pair,
            "buy_order": group_order(orderbook["bids"]),
            "sell_order": group_order(orderbook["asks"])
        }
        r.set(current_time+"-"+trading_pair+"-BINANCE-Orderbook", json.dumps(trade_data),ex=86400)#1d过期

def start_binance_websocket():
    symbol = binance_token_array[0]
    initialize_orderbook(symbol)
    threading.Thread(target=save_snapshoot,args=(binance_get_orderbook_interval,)).start()